package com.example.extra.utils;

import com.example.extra.common.CustomUserDetails;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;


import javax.crypto.SecretKey;

import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author lixiang
 */
@Slf4j
@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.expiration}")
    private long jwtExpirationMs;

    @Value("${jwt.refresh-token.expiration}")
    private long refreshTokenExpirationMs;

    @Autowired
    private RedisTemplate redisTemplate;

    // Redis中存储用户最新令牌的key前缀
    private static final String USER_LATEST_TOKEN_PREFIX = "user:latest:token:";

    private static final String USER_LATEST_REFRESH_TOKEN_PREFIX = "user:latest:refresh_token:";

    // 生成访问令牌
    public String generateAccessToken(CustomUserDetails userDetails) {
        //CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpirationMs);

        List<String> roles = userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());

        String token = Jwts.builder()
                .header().add("typ", "JWT").and()
                .subject(userDetails.getUsername())
                .claim("userId", userDetails.getId())
                .claim("username", userDetails.getUsername())
                .claim("roles", roles)
                .claim("nickname", userDetails.getNickname())
                .issuedAt(now)
                .expiration(expiryDate)
                .id(UUID.randomUUID().toString())
                .signWith(getHmacKey(), Jwts.SIG.HS256)
                .compact();

        //新登录的人挤掉上一次登录的人
        String username = userDetails.getUsername();
        String redisKey = USER_LATEST_TOKEN_PREFIX + username;
        redisTemplate.opsForValue().set(redisKey, token, jwtExpirationMs, TimeUnit.MILLISECONDS);
        return token;
    }
     // 从 Token 中提取用户名
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    // 提取特定的声明 (Claim)
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
// 解析 Token 获取所有声明
    private Claims extractAllClaims(String token) {
        return Jwts.parser()
                .verifyWith(getHmacKey()) // 使用密钥验证签名
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
    private  SecretKey getHmacKey() {
        // 实际应用中，密钥应从安全配置（如环境变量、密钥管理服务）获取
        return Keys.hmacShaKeyFor(jwtSecret.getBytes()); // 生成 HMAC 密钥
    }

     // 验证令牌
    public boolean validateToken(String token) {
        try {
//            Jws<Claims> claimsJws = Jwts.parser()
//                    .verifyWith(getHmacKey())
//                    .build()
//                    .parseSignedClaims(token);

            final String username = extractUsername(token);
            // 验证令牌是否为Redis中存储的最新版本
            String redisKey = USER_LATEST_TOKEN_PREFIX + username;
            String latestToken = redisTemplate.opsForValue().get(redisKey)+"";

            return !isTokenExpired(token) && token.equals(latestToken);

        } catch (MalformedJwtException e) {
            log.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            log.error("JWT token is expired: {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
            log.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("JWT claims string is empty: {}", e.getMessage());
        }
        return false;
    }
     // 检查 Token 是否过期
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    // 从 Token 中提取过期时间
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
    // 验证 Token 时检查黑名单
    public Boolean isTokenBlacklisted(String token) {
        Claims claims = extractAllClaims(token);
        String jti = claims.getId(); // 或者 (String) claims.get("jti")
        return Boolean.TRUE.equals(redisTemplate.hasKey("blacklist:" + jti));
    }
    // 从令牌中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .verifyWith(getHmacKey())
                .build()
                .parseSignedClaims(token)
                .getPayload()
                .getSubject();
    }

    // 从请求中获取令牌
    public String parseToken(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");

        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7);
        }

        return null;
    }
}
